#include "GL/glut.h"
#include <iostream>
#include "FreeImage.h"
#include "time.h"
#include <cmath>
//#include <strstream>
//#include "bmpReader.h"

using namespace std;

long frameclock = 0;
float angle=0.0;
float radius=3.0f;
int sleepTime=30;

float eyePosZ = 1.2f;
float rotateable=0.05;

//imageMM *texImage;

bool needLightlSet=true;
bool needMaterialSet=true;

bool needTextureSet=true;
GLint textureModeSet=GL_REPLACE;

bool needFragmentBlend=false;

GLuint textureNum;
GLfloat zZeroPlane[] = {0.0f, 0.0f, 1.0f, 0.0f};
GLfloat xZeroPlane[] = {1.0f, 0.0f, 0.0f, 0.0f};

/*
void initialTexture_old()
{
	texImage = new imageMM(std::string("mse.bmp")); 

	glGenTextures(1,(&textureNum));
	glBindTexture(GL_TEXTURE_2D,textureNum);

	glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,
				 texImage->getWidth(),texImage->getHeight(),
				 0,GL_RGB,GL_UNSIGNED_BYTE,
				 texImage->getImageData());

	//*
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);	
	
	GLfloat envColor[]={0.8f,0.8f,0.8f,0.8f};
	glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,envColor);	
	
}
//*/

void initialTexture()
{
	//texImage = new imageMM(std::string("mse.bmp")); 
	
	FreeImage_Initialise();

	FIBITMAP * dib = FreeImage_Load(FIF_BMP, "mse.bmp", BMP_DEFAULT);

	FreeImage_GetWidth(dib),FreeImage_GetHeight(dib);

	dib = FreeImage_ConvertTo24Bits(dib);

	//dib = FreeImage_Rescale(dib, 512, 512, FILTER_BILINEAR);

	BYTE *ptr = FreeImage_GetBits(dib);

	glGenTextures(1,(&textureNum));
	glBindTexture(GL_TEXTURE_2D,textureNum);

	glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,
				FreeImage_GetWidth(dib), 
				FreeImage_GetHeight(dib), 
				0,
				GL_RGB,
				GL_UNSIGNED_BYTE,
				ptr);

	//*
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);	
	
	GLfloat envColor[]={0.8f,0.8f,0.8f,0.8f};
	glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,envColor);	
	
}

void applyTex()
{
	glEnable(GL_TEXTURE_2D);
}

void fragmentBlendSet()
{	
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
}

void materialSet()
{
	GLfloat matColor[]={0.75f,0.4f,0.4f,0.5f};
	glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,matColor);

	GLfloat specref[]={1.0f,1.0f,1.0f,0.5f};	
	glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specref);

	glMateriali(GL_FRONT_AND_BACK,GL_SHININESS,128);		
}

void lightSet()
{
	GLfloat amlight[]={0.3,0.0,0.3,1.0};
	GLfloat dflight[]={0.7,0.0,0.7,1.0};
	GLfloat specular[] = {1.0f,1.0f,1.0f,1.0f};

	glLightfv(GL_LIGHT0,GL_AMBIENT,amlight);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,dflight);
	glLightfv(GL_LIGHT0,GL_SPECULAR,specular);
	glLightf(GL_LIGHT0,GL_SPOT_CUTOFF, 60.0);	
	GLfloat lightpos[]={0.6,0.6,0.6,1.0};
	glLightfv(GL_LIGHT0,GL_POSITION,lightpos);

	glEnable(GL_LIGHT0);

	//glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
	glEnable(GL_LIGHTING);
}

void TextureSet()
{}

void colorMaterialMode()
{
	glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);

	glEnable(GL_COLOR_MATERIAL);

	GLfloat specref[]={0.0f,0.0f,0.0f,1.0};	
	glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specref);
	glMateriali(GL_FRONT_AND_BACK,GL_SHININESS,0);		
}

void display()
{
	if(needMaterialSet)
	{
		materialSet();
	}
	if(needLightlSet)
	{
		lightSet();
	}
	else
	{
		glDisable(GL_LIGHTING);
	}

	if(needMaterialSet)
	{
		glDisable(GL_COLOR_MATERIAL);
	}
	else
	{
		colorMaterialMode();
	}

	glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,textureModeSet);
	needFragmentBlend	?fragmentBlendSet()										:glDisable(GL_BLEND);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer

	if(rotateable)
	{
		long step = clock() - frameclock;
		angle = 3.1416*(double(step)/CLOCKS_PER_SEC);
		//angle=angle > 6.2832?angle-6.2832:angle;
	}
	//glPushAttrib
	glPushMatrix();
	gluLookAt(radius*cos(angle), radius*sin(angle), eyePosZ,
			  0.0, 0.0, 0.0,	
			  0.0, 0.0, 1.0);

	glColor3f(1.0, 0.0, 0.0);
	glBegin(GL_LINES);
	glVertex3f(0.0,0.0,0.0);
	glVertex3f(1.0,0.0,0.0);   
	glEnd();

	glColor3f(0.0, 1.0, 0.0);
	glBegin(GL_LINES);
	glVertex3f(0.0,0.0,0.0);
	glVertex3f(0.0,1.0,0.0);
	glEnd();

	glColor3f(0.0, 0.0, 1.0);
	glBegin(GL_LINES);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 1.0);
	glEnd();	

	//
	if(needTextureSet)	
	{
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_S, GL_OBJECT_PLANE, xZeroPlane);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_T, GL_OBJECT_PLANE, zZeroPlane);	

		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
	}
	else
	{
		glDisable(GL_TEXTURE_2D);
	}	

	glColor4f(0.6, 0.6, 0.6, 1.0);	
	glutSolidSphere(0.3,100,100);		

	/*glBegin(GL_POLYGON);
	glVertex3f(1.0,0.0,0.0);
	glVertex3f(1.0,6,0);
	glVertex3f(1.0,4,3);
	glVertex3f(1.0,2,3);
	glEnd();*/

	glBegin(GL_TRIANGLES);
	glVertex3f(0.6,0.0,1.0);
	glVertex3f(0.0,0.6,1.0);
	glVertex3f(0.0,0.0,1.6);
	glEnd();


	glColor4f(0.4,0.4,0.4, 0.2);
	glTranslatef(0.0,0.0,-0.9);
	glutSolidCone(0.4, 0.6, 10, 10);
	glTranslatef(0.0,0.0,0.9);

	glColor4f(0.2,0.2,0.2,0.5);
	if(needTextureSet)
	{
		glEnable(GL_TEXTURE_2D);
	}

	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);

	glBegin(GL_TRIANGLES);
	if(needTextureSet)
	{
		applyTex();
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(0.6,0.0,0.0);

		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(0.0,0.6,0.0);

		glTexCoord2f(0.5f, 1.0f);
		glVertex3f(0.0,0.0,0.6);
	}
	else
	{
		glVertex3f(0.6,0.0,0.0);
		glVertex3f(0.0,0.6,0.0);
		glVertex3f(0.0,0.0,0.6);
	}
	glEnd();

	glutSwapBuffers();
	//Sleep(sleepTime);

	glPopMatrix();
}

void draw()
{
	display();
}

void KeyFunc(unsigned char key, int x, int y)
{
	switch(key)
	{
		case 'u':
		case 'U':
		{
			eyePosZ += 0.05;
		}
		break;

		case 'd':
		case 'D':
		{
			eyePosZ -= 0.05;
		}
		break;
	}
}

const char* nowTexMode()
{
	const char * str;
	textureModeSet==GL_REPLACE?str="GL_REPLACE":
		textureModeSet==GL_MODULATE?str="GL_MODULATE":
			textureModeSet==GL_BLEND?str="GL_BLEND":str="GL_DECAL";
	return str;
}

/*
void renderStateShow()
{
	std::ostrstream infOutPut;
	infOutPut<<"光照设置     -- "<<(needLightlSet		?"光照模型"		:"颜色模型"		)<<endl;
	infOutPut<<"材质设置     -- "<<(needMaterialSet		?"自定义材质"	:"颜色材质模式"	)<<endl<<endl;
	
	infOutPut<<"纹理映射     -- "<<(needTextureSet		?"纹理映射"		:"无纹理映射"	)<<endl;	
	infOutPut<<"纹理模式     -- "<<nowTexMode()<<endl<<endl;

	infOutPut<<"片元混合     -- "<<(needFragmentBlend	?"打开混合"		:"关闭混合"		)<<endl;
	infOutPut<<ends;

	MessageBox(NULL,infOutPut.str(),"当前绘制状态",MB_OK);
}
//*/

/* 定义菜单的响应函数 */ 
#define lightSetting 0

#define materialSetting 1
#define fragmentBlendSetting 2

#define textureSetting 3
#define textureBlendSetting 4

#define emptymenu 99
#define currenStateShow 100
#define rotateSetting 101

void menuCreate( int entry ) 
{ 
	switch ( entry ) 
	{ 
		case lightSetting: 
			needLightlSet?needLightlSet=false:needLightlSet=true;  
			break; 
		case materialSetting: 
			needMaterialSet?needMaterialSet=false:needMaterialSet=true;  
			break; 

		case textureSetting: 
			needTextureSet?needTextureSet=false:needTextureSet=true;  
			break; 
		case textureBlendSetting: 			
			textureModeSet==GL_REPLACE?textureModeSet=GL_MODULATE:
				textureModeSet==GL_MODULATE?textureModeSet=GL_BLEND:
					textureModeSet==GL_BLEND?textureModeSet=GL_DECAL:textureModeSet=GL_REPLACE;
			break;

		case fragmentBlendSetting: 
			needFragmentBlend?needFragmentBlend=false:needFragmentBlend=true;  
			break; 
		case rotateSetting:
			rotateable<0.02f?rotateable=0.05f:rotateable=0.0f;
			break;

		default :
			break;
	}

	/* 选择菜单后重绘OpenGL窗口 */

	if(entry!=emptymenu)
	{
		//renderStateShow();
	}

	glutPostRedisplay(); 
} 

void initialMenu()
{
	/* 定义菜单响应函数*/ 
	glutCreateMenu(menuCreate);

	//glutAddMenuEntry("光照模式", lightSetting);
	//glutAddMenuEntry("材质设定", materialSetting);	
	glutAddMenuEntry("lighting mode", lightSetting);
	glutAddMenuEntry("matiral mode", materialSetting);	
	glutAddMenuEntry("", emptymenu);

	glutAddMenuEntry("texture use", textureSetting);
	glutAddMenuEntry("texture mode", textureBlendSetting);	
	glutAddMenuEntry("", emptymenu);

	glutAddMenuEntry("blend mode", fragmentBlendSetting);
	glutAddMenuEntry("", emptymenu);

	//glutAddMenuEntry("查看当前状态", currenStateShow);
	glutAddMenuEntry("rotate", rotateSetting);

	/* 定义菜单动作方式为 点击右键弹出 */ 
	glutAttachMenu( GLUT_RIGHT_BUTTON ); 
}

int main()
{	
	int   argc=0;
	char  *argv[10]; 

	frameclock = 0;
	/*
	frameclock = clock();
	cout<<frameclock<<endl;
	for(int i=0;i<100;++i)
	{
		usleep(100000);
		frameclock = clock();
		cout<<i<<" "<<frameclock<<endl;
	}
	cout<<"CLOCKS_PER_SEC"<<CLOCKS_PER_SEC<<endl;
	return 0;
	//*/

	glutInit(&argc, argv);

	//glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800,600);
	glutInitWindowPosition(100,100);
	//glutCreateWindow(" openGL sample by zjn --- 点击右键改变绘制状态 u键d键改变视点位置 ");
	glutCreateWindow(" openGL sample by zjn --- right click to change draw stat,press u and d to up and down view point ");

	glClearColor(0.2, 0.4, 0.6, 1.0);
	glViewport(0,0,800,600);						// Reset The Current Viewport
	glShadeModel(GL_SMOOTH);

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix
	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)800/(GLfloat)600,0.5f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();		

	glEnable(GL_DEPTH_TEST);

	initialTexture();	

	//glEnable(GL_BLEND);
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	initialMenu();
	
	glutKeyboardFunc(KeyFunc);

	glutDisplayFunc(display);	

	glutIdleFunc(draw);

	glutMainLoop();
	
	return 0;
}
